var box2d = {
   b2Vec2 : Box2D.Common.Math.b2Vec2,
   b2BodyDef : Box2D.Dynamics.b2BodyDef,
   b2Body : Box2D.Dynamics.b2Body,
   b2FixtureDef : Box2D.Dynamics.b2FixtureDef,
   b2Fixture : Box2D.Dynamics.b2Fixture,
   b2World : Box2D.Dynamics.b2World,
   b2MassData : Box2D.Collision.Shapes.b2MassData,
   b2PolygonShape : Box2D.Collision.Shapes.b2PolygonShape,
   b2CircleShape : Box2D.Collision.Shapes.b2CircleShape,
   b2DebugDraw : Box2D.Dynamics.b2DebugDraw
};

//shorthand for some b2Vec2s
var v = {
    up : new box2d.b2Vec2(0,1),
    down : new box2d.b2Vec2(0,-1),
    left : new box2d.b2Vec2(-1,0),
    right : new box2d.b2Vec2(1,0),
    topLeft : new box2d.b2Vec2(1,-1),
    topRight : new box2d.b2Vec2(1,1),
    bottomLeft : new box2d.b2Vec2(-1,-1),
    bottomRight : new box2d.b2Vec2(-1,1),
    zero : new box2d.b2Vec2(0,0)
}

// Input axes
var input = {
	xAxisLeft : 0,
	yAxisLeft : 0, 
	xAxisRight : 0,
	yAxisRight : 0
};
	

var SCALE = 30;
var stage, world, player, enemy, canvas, debug_canvas, level;
var bulletList = new Array();
var draw_debug = true;

function init() {
	canvas = document.getElementById('canvas');
	debug_canvas = document.getElementById('debug_canvas');
	
	stage = new createjs.Stage(canvas);
	//debug_stage = new createjs.Stage(debug_canvas);
	
	setupPhysics();
	setupImages();
	setupLevel();
	
	player = new Player();
	enemy = new Enemy();
	
	// add event listeners, stop default shortcuts for web browsers and IE
        // Chrome > 4 will still capture ctr+w among others :<
        // todo: maybe allow propagation for specific events
	window.addEventListener('keydown', Event.prototype.stopPropagation ? 
                function(event) { 
			Key.onKeyDown(event);
			if (event.keyCode != 74) {  // 'j' shall pass! (so we can still use ctrl+shift+j to show the js console in chrome)
				event.stopPropagation;
				event.preventDefault();
			}
		} : function(event) { Key.onKeyDown(event); event.cancelBubble=true; } 
                , false);
	window.addEventListener('keyup', Event.prototype.stopPropagation ? 
                function(event) { 
			Key.onKeyUp(event); event.stopPropagation; event.preventDefault(); } : 
                function(event) { Key.onKeyUp(event); event.cancelBubble=true; } 
                , false);
		
        //annoy Chrome users who want to actually exit the page
        if (navigator.userAgent.indexOf('Chrome') != -1) {
            window.onbeforeunload = function(evt) {return 'Did you accidentally a shortcut or do you really want to leave?';};
        }


	// setting up the Ticker
	createjs.Ticker.addListener(window);
	createjs.Ticker.setFPS(60);
	createjs.Ticker.useRAF = true;
}

function setupPhysics() {
	// create the box2d world reference
	world = new box2d.b2World(new box2d.b2Vec2(0,50), true);
	
	// setup debug draw (box2d)
	var debugDraw = new box2d.b2DebugDraw();
	debugDraw.SetSprite(debug_canvas.getContext('2d'));
	debugDraw.SetFillAlpha(0.4);
	debugDraw.SetDrawScale(SCALE);
	debugDraw.SetFlags(box2d.b2DebugDraw.e_shapeBit | box2d.b2DebugDraw.e_jointBit);
	world.SetDebugDraw(debugDraw);
	
	
	/* should be moved from physics setup */
	
	//Add listeners for contact
	var listener = new Box2D.Dynamics.b2ContactListener;

	listener.BeginContact = function(contact) {
	    //console.log(contact.GetFixtureA().GetBody().GetUserData());
	}

	listener.EndContact = function(contact) {
	    // console.log(contact.GetFixtureA().GetBody().GetUserData());
	}

	listener.PostSolve = function(contact, impulse) {
		
	    if (contact.GetFixtureA().GetBody() == player.playerBody/* || contact.GetFixtureB().GetBody() == player.playerBody*/) {
		var worldManifold = new Box2D.Collision.b2WorldManifold
		contact.GetWorldManifold(worldManifold)
		    //console.log(worldManifold)
                player.handleContact(contact,worldManifold);

		
	    }
	   // console.log(player.playerBody)
	}
	
	listener.PreSolve = function(contact, oldManifold) {
	    // PreSolve
	}

	world.SetContactListener(listener);
}

function setupImages() {
}

function setupLevel() {
	level = new Level();
}

function tick() {
	stage.update();
	
	handleKeys();
	
	player.update();
	enemy.update();
	
	if(draw_debug) {
		world.DrawDebugData();
	}
	updateBulletList();
	world.Step(1/60, 10, 10);
	world.ClearForces();
	
}

function updateBulletList() {
	for(var i = 0; i < bulletList.length; i++) {
		var bullet = bulletList[i]
		//set up input
		var input = new Box2D.Collision.b2RayCastInput();
		input.p1 = new box2d.b2Vec2(bullet.x,bullet.y);
		input.p2 = new box2d.b2Vec2(bullet.x,bullet.y);
		input.p2.Add(bullet.heading);
		input.maxFraction = bullet.speed;
	  
		//check every fixture of every body to find closest
		var closestFraction = input.maxFraction; //start with end of line as p2
		var intersectionNormal;
		var closestBody;
		var closestFixure;
		
		
		/* Here we search for the nearest fixure in the path from the current location to
		 * the next location of the bullet using the raycast method.
		 * Continous detection as the bullet might be inside a fixure using QueryPoint function
		 * TODO: Override ContactFilter to include real z filtering.
		*/
		
		closestFraction = bullet.speed;
		intersectionNormal = 0;
		closestFixure = 0;
		
		var callback = function (fixure) {
			if(fixure.GetBody() != bullet.origin) {
				var direction = fixure.GetBody().GetLocalPoint(fixure.GetBody().GetWorldCenter())	
				
				closestFraction = 0;
				intersectionNormal = direction;
				closestFixure = fixure;
				return false;
			}
			return true;
		}
		world.QueryPoint(callback, input.p2);
		
		var callback = function (fixure/*b2Fixture*/, point/*b2Vec2*/, normal/*b2Vec2*/, fraction/*float*/) {
			if(fixure.GetBody() == bullet.origin) return -1;
			if(fraction < closestFraction) {
				closestFraction = fraction;
				intersectionNormal = normal;
				closestFixure = fixure;
			}
			return closestFraction;
		};
		world.RayCast(callback,input.p1, input.p2);
		
		
		// The end point of the bullet line / may be an intersection point with a fixure
		var endPoint = new box2d.b2Vec2(input.p2.x,input.p2.y);
		endPoint.Subtract(input.p1);
		endPoint.Multiply(closestFraction);
		endPoint.Add(input.p1);
		
		//draw a line (TODO; replace direct draw on canvas?)
		//debugDraw.DrawSegment(input.p1, intersectionPoint, new Box2D.Common.b2Color(1,0,1))
		var ctx = canvas.getContext('2d');
		ctx.strokeStyle = '#ffA500';
		ctx.lineWidth=4;
		ctx.beginPath();
		ctx.moveTo(bullet.x * SCALE, bullet.y * SCALE);		
		ctx.lineTo(endPoint.x * SCALE, endPoint.y * SCALE);
		ctx.stroke();
		
		bullet.x += bullet.heading.x*closestFraction;
		bullet.y += bullet.heading.y*closestFraction;
		
		if(closestFraction < bullet.speed) { // we hit something :D
			closestFixure.GetBody().ApplyImpulse(new box2d.b2Vec2(bullet.heading.x*15,bullet.heading.y*15), closestFixure.GetBody().GetWorldCenter());
			this.bulletList.splice(i,1);
			world.DestroyBody(bullet);
			document.getElementById('debug').innerHTML = this.bulletList.length;
			i--;
		}
	}
	
	if(this.bulletList.length > 100) {
		var bullet = this.bulletList[0];
		this.bulletList.splice(0,1);
		world.DestroyBody(bullet);
		document.getElementById('debug').innerHTML = this.bulletList.length;
	}
}

function handleKeys() {
	
	if(!Key.isDown(Key.A) && !Key.isDown(Key.D)) input.xAxisLeft = 0;
	else {
		if(Key.isDown(Key.A)) input.xAxisLeft = -1;
		if(Key.isDown(Key.D)) input.xAxisLeft = 1;
	}

		

	if(!Key.isDown(Key.W) && !Key.isDown(Key.S)) input.yAxisLeft = 0;
	else {
		if(Key.isDown(Key.W)) input.yAxisLeft = 1;
		if(Key.isDown(Key.S)) input.yAxisLeft = -1;
	}


	if(!Key.isDown(Key.LEFT) && !Key.isDown(Key.RIGHT)) input.xAxisRight = 0;
	else {
		if(Key.isDown(Key.LEFT)) input.xAxisRight = -1;
		if(Key.isDown(Key.RIGHT)) input.xAxisRight = 1;
	}


	if(!Key.isDown(Key.UP) && !Key.isDown(Key.DOWN)) input.yAxisRight = 0;
	else {
		if(Key.isDown(Key.UP)) input.yAxisRight = 1;
		if(Key.isDown(Key.DOWN)) input.yAxisRight = -1;
	}
}

function enable_debug() {
	draw_debug = !draw_debug;
	debug_canvas.getContext('2d').clearRect(0 , 0 , 800 , 600);
}

function togglePause() {
	var doPause = !createjs.Ticker.getPaused();
	if(doPause) {
		var ctx = canvas.getContext('2d');
		ctx.font = "bold 18px sans-serif";
		ctx.fillText("- Paused -", 370, 270);
	} else {
		
	}
	createjs.Ticker.setPaused(doPause)
}

var Key = {
	_pressed: {
		80:false
	},

	A: 65,
	W: 87,
	D: 68,
	S: 83,
	LEFT: 37,
	UP: 38,
	RIGHT: 39,
	DOWN: 40, 
	
	isDown: function(keyCode) {
		return this._pressed[keyCode];
	},
	
	onKeyDown: function(event) {
		if(event.keyCode == 80) { // pause - P
			if(this._pressed[event.keyCode] == false) // first press
				togglePause();
			return;
		}
		
		this._pressed[event.keyCode] = true;
		//console.log(this._pressed);
		
		//prevent axis from spazzing out when opposite buttons are pressed simultaneously - the last button press should always count
		if (this._pressed[65] && event.keyCode == 68) this._pressed[65] = false;
		if (this._pressed[68] && event.keyCode == 65) this._pressed[68] = false;
		
		if (this._pressed[37] && event.keyCode == 39) this._pressed[37] = false;
		if (this._pressed[39] && event.keyCode == 37) this._pressed[39] = false;
		
		if (this._pressed[38] && event.keyCode == 40) this._pressed[38] = false;
		if (this._pressed[40] && event.keyCode == 38) this._pressed[40] = false;
		

		if (event.keyCode == 82) {   // reload without popup on <r> and <ctrl+r>
			window.onbeforeunload = null;
			location.reload();
		}
	},
	  
	onKeyUp: function(event) {
		this._pressed[event.keyCode] = false;
	}
};
